﻿// Controller Class

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using System.Runtime.InteropServices;

using Orchard.Model;
using Orchard.View;

namespace Orchard.Controller
{
    class OrchardController
    {
        OrchardModel model;
        OrchardView view;
        Cursor axeCursor;

        Image SelectModeAxe;
        Image SelectModeAxeShadow;
        Image AxeModeAxe;
        Image AxeModeEndAxe;
        
        // Constructor
        public OrchardController(OrchardModel controllerModel, OrchardView controllerView)
        {
            //controller keeps instance of the model and view
            model = controllerModel;
            view = controllerView;

            SelectModeAxe = new Bitmap(Orchard.Properties.Resources.AxeWithSilverHooks);
            SelectModeAxeShadow = new Bitmap(Orchard.Properties.Resources.AxeStartIconShadow);
            SelectModeAxeShadow.RotateFlip(RotateFlipType.Rotate270FlipNone);
            AxeModeAxe = new Bitmap(Orchard.Properties.Resources.SilverHooksNoAxe);
            AxeModeEndAxe = new Bitmap(Orchard.Properties.Resources.SetDownAxe3);

            view.AxeIcon.Image = SelectModeAxe;

            view.exerciseTreeControl.Tree = model.ExerciseTree;
            view.learningTreeControl.Tree = model.LearningTree;
            view.productivityTreeControl.Tree = model.ProductivityTree;

            #region Add Event Listeners

            //add listeners to components
            view.productivityTreeControl.Click += new EventHandler(ProductivityTreeSelect);
            view.learningTreeControl.Click += new EventHandler(LearningTreeSelect);
            view.exerciseTreeControl.Click += new EventHandler(ExerciseTreeSelect);
            view.HomeNextDay.Click += new EventHandler(NextDayButton_Click);

            view.ProductivityControl.HomeButton.Click += new EventHandler(HomeButton_Click);
            view.ProductivityControl.DrawingBox.Paint += new PaintEventHandler(DrawingBox_Paint);
            view.ProductivityControl.CheckBoxCollection.ItemCheck += new ItemCheckEventHandler(Collection_SelectionChanged);
            view.ProductivityControl.SubmitButton.Click += new EventHandler(SubmitButton_Click);

            view.LearningControl.HomeButton.Click += new EventHandler(HomeButton_Click);
            view.LearningControl.DrawingBox.Paint += new PaintEventHandler(DrawingBox_Paint);
            view.LearningControl.CheckBoxCollection.ItemCheck += new ItemCheckEventHandler(Collection_SelectionChanged);
            view.LearningControl.SubmitButton.Click += new EventHandler(SubmitButton_Click);

            view.ExerciseControl.HomeButton.Click += new EventHandler(HomeButton_Click);
            view.ExerciseControl.DrawingBox.Paint += new PaintEventHandler(DrawingBox_Paint);
            view.ExerciseControl.CheckBoxCollection.ItemCheck += new ItemCheckEventHandler(Collection_SelectionChanged);
            view.ExerciseControl.SubmitButton.Click += new EventHandler(SubmitButton_Click);

            view.AxeIcon.Click += new EventHandler(this.BeginAxeMode);
            view.AxeIcon.MouseEnter += new EventHandler(this.AddSelectModeAxeShadow);
            view.AxeIcon.MouseLeave += new EventHandler(this.RemoveSelectModeAxeShadow);
            #endregion

            // Create the axe cursor
            axeCursor = new Cursor("./Resources/axe.cur");
        }

        public void startApplication()
        {
            //initialize controls
            resetControls();

            //start and display the window
            Application.Run(view);
        }

        #region Event Listeners
        void DrawingBox_Paint(object sender, PaintEventArgs e)
        {
            //determine which panel is currently being displayed
            TreeTypeEnum displayed = view.currentlyDisplayed();
            int centerX;
            int centerY;

            switch (displayed)
            {
                case TreeTypeEnum.ProductivityTree:
                    //determine center point
                    centerX = view.ProductivityControl.DrawingBox.Width / 2;
                    centerY = view.ProductivityControl.DrawingBox.Height / 2;

                    //draw the rings on the screen
                    drawRings(e.Graphics, new Point(centerX, centerY), model.ProductivityTree.Rings);
                break;

                case TreeTypeEnum.LearningTree:
                    //determine center point
                    centerX = view.LearningControl.DrawingBox.Width / 2;
                    centerY = view.LearningControl.DrawingBox.Height / 2;

                    //draw the rings on the screen
                    drawRings(e.Graphics, new Point(centerX, centerY), model.LearningTree.Rings);
                break;

                case TreeTypeEnum.ExerciseTree:
                    //determine center point
                    centerX = view.ExerciseControl.DrawingBox.Width / 2;
                    centerY = view.ExerciseControl.DrawingBox.Height / 2;

                    //draw the rings on the screen
                    drawRings(e.Graphics, new Point(centerX, centerY), model.ExerciseTree.Rings);
                break;
            }
        }

        //private void drawRings(Graphics g, Point centerLocation, List<Ring> thicknesses)
        //{
        //    //get images
        //    Image treeBark = Properties.Resources.bark;
        //    Image treeInside = Properties.Resources.treeInside;

        //    //determine center coordinates
        //    int centerX = centerLocation.X - 5;
        //    int centerY = centerLocation.Y - 5;

        //    //create brush for drawing
        //    SolidBrush treeColor = new SolidBrush(Color.FromArgb(84, 64, 41));

        //    //draw the bark
        //    g.DrawImage(treeBark, centerX - 5 * thicknesses.Count - 5, centerY - 5 * thicknesses.Count - 5, thicknesses.Count * 10 + 20, thicknesses.Count * 10 + 20);

        //    //draw the insides
        //    g.DrawImage(treeInside, centerX - 5 * thicknesses.Count, centerY - 5 * thicknesses.Count, thicknesses.Count * 10 + 10, thicknesses.Count * 10 + 10);

        //    //draw the core
        //    g.FillEllipse(treeColor, centerX, centerY, 10, 10);

        //    //draw the rings
        //    Pen ringPen = new Pen(treeColor);
        //    for (int i = 0; i < thicknesses.Count; i++)
        //    {
        //        //don't draw anything for 0 values
        //        if (thicknesses[i].Value == 0)
        //        {
        //            //adjust for next coordinates
        //            centerX -= 5;
        //            centerY -= 5;

        //            continue;
        //        }

        //        //adjust pen width
        //        ringPen.Width = thicknesses[i].Value / 3;

        //        //g.DrawEllipse(ringPen, centerX - radius, centerY - radius, radius, radius);
        //        g.DrawEllipse(ringPen, centerX, centerY, i * 10 + 10, i * 10 + 10);

        //        //adjust for next coordinates
        //        centerX -= 5;
        //        centerY -= 5;
        //    }
        //}

        private void drawRings(Graphics g, Point centerLocation, List<Ring> thicknesses)
        {
            //get images
            Image treeBark = Properties.Resources.bark;
            Image treeInside = Properties.Resources.treeInside;

            //determine center coordinates
            int centerX = centerLocation.X;
            int centerY = centerLocation.Y;

            //create brush for drawing
            SolidBrush treeColor = new SolidBrush(Color.FromArgb(84, 64, 41));

            //draw the rings
            Pen ringPen = new Pen(treeColor);

            //starting radius equal to the core
            int radius = 5 + 1;
            int width = 0;

            //determine what the max radius will be
            for (int i = 0; i < thicknesses.Count; i++)
            {
                if (thicknesses[i].Value != 0)
                {
                    width = thicknesses[i].Value / 2 + 1;
                    for (int ring = 0; ring < width; ring++)
                    {
                        radius++;
                    }
                    
                    //////////////////////////////////
                    //radius += 2;
                    //////////////////////////////////
                    radius += thicknesses[i].Spacing;
                }
            }

            //draw outer bark
            drawPicture(treeBark, g, centerX + 5, centerY + 5, radius + 5);

            //draw inner texture
            drawPicture(treeInside, g, centerX + 5, centerY + 5, radius);

            //draw the core
            g.FillEllipse(treeColor, centerX, centerY, 10, 10);
            
            //adjust for core
            centerX += 5;
            centerY += 5;

            //reset radius
            radius = 5 + 1;

            //go through each line width
            for (int i = 0; i < thicknesses.Count; i++)
            {
                //only draw rings for days they did something
                if (thicknesses[i].Value != 0)
                {
                    //adjust pen width based on their rating for the day
                    //ringPen.Width = thicknesses[i].Value / 2;
                    //drawCircle(ringPen, g, centerX, centerY, radius);
                    ringPen.Width = 2;

                    //determine width and draw appropriate number of circles for width
                    width = thicknesses[i].Value / 2 + 1;
                    for (int ring = 0; ring < width; ring++)
                    {
                        drawCircle(ringPen, g, centerX, centerY, radius);
                        radius++;
                    }

                    //offset based on how large the ring was
                    //radius += Int32.Parse(ringPen.Width.ToString()) + 1;

                    //////////////////////////////////
                    //radius += 2;
                    //////////////////////////////////
                    radius += thicknesses[i].Spacing;
                }
            }
        }

        void drawCircle(Pen pen, Graphics g, int centerX, int centerY, int radius)
        {
            //draw a circle at the specified center point
            g.DrawEllipse(pen, centerX - radius, centerY - radius, radius * 2, radius * 2);
        }

        void drawPicture(Image picture, Graphics g, int centerX, int centerY, int radius)
        {
            //draw an image centered on the specified point
            g.DrawImage(picture, centerX - radius, centerY - radius, radius * 2, radius * 2);
        }

        void SubmitButton_Click(object sender, EventArgs e)
        {
            //determine which panel is currently being displayed
            TreeTypeEnum displayed = view.currentlyDisplayed();
            int value;
            DateTime dateAdded;

            switch (displayed)
            {
                //process submit to productivity tree
                case TreeTypeEnum.ProductivityTree:
                    //determine user's productivity and which date the entry is for
                    value = Int32.Parse(view.ProductivityControl.InputSpinner.Value.ToString());
                    dateAdded = model.ProductivityDate;

                    //add ring to the tree
                    model.ProductivityTree.addRing(new Ring(value, dateAdded));

                    //redraw the rings
                    view.ProductivityControl.DrawingBox.Refresh();

                    //update records and disable controls if up-to-date
                    resetControls();
                    model.ProductivityDate = model.ProductivityDate.AddDays(1);
                    view.displayProductivityPanel();
                break;

                //process submit to learning tree
                case TreeTypeEnum.LearningTree:
                    //determine which date the entry is for
                    value = 0;
                    dateAdded = model.LearningDate;

                    //create new ring with the added date
                    Ring newRing = new Ring(dateAdded);

                    //determine if they learned anything today
                    if (view.LearningControl.CheckBoxCollection.GetItemCheckState(1) == CheckState.Checked)
                    {
                        newRing.randomizeAttributes();
                    }

                    //add ring to the tree
                    model.LearningTree.addRing(newRing);

                    //redraw the rings
                    view.LearningControl.DrawingBox.Refresh();

                    //update records and disable controls if up-to-date
                    resetControls();
                    model.LearningDate = model.LearningDate.AddDays(1);
                    view.displayLearningPanel();
                break;

                //process submit to exercise tree
                case TreeTypeEnum.ExerciseTree:
                    //determine how long they exercised for and which date the entry is for
                    value = Int32.Parse(view.ExerciseControl.InputSpinner.Value.ToString());
                    dateAdded = model.ExerciseDate;

                    //normalize value to a 1-10 scale
                    double ratioValue = (value / 60.0) * 10.0;
                    int exerciseValue = (int)ratioValue;

                    //if they only did 5 minutes, count it as something
                    if (value == 5)
                    {
                        exerciseValue = 1;
                    }

                    //add ring to the tree
                    model.ExerciseTree.addRing(new Ring(exerciseValue, dateAdded));

                    //redraw the rings
                    view.ExerciseControl.DrawingBox.Refresh();

                    //update records and disable controls if up-to-date
                    resetControls();
                    model.ExerciseDate = model.ExerciseDate.AddDays(1);
                    view.displayExercisePanel();
                break;
            }
        }

        void resetControls()
        {
            //reset and disable all controls
            resetCollectionBoxes();
            resetSpinners();
            spinnersEnabledState(true);
        }

        void HomeButton_Click(object sender, EventArgs e)
        {
            //go back to the main screen
            view.displayHomePanel();

            //reset all panel controls
            resetControls();
        }

        public void Collection_SelectionChanged(object sender, ItemCheckEventArgs e)
        {
            //if they're checking a box and more boxes can't be checked
            if (e.NewValue == CheckState.Checked && !CollectionCanCheck())
            {
                e.NewValue = CheckState.Unchecked;
            }

            //if they're changing the off day box
            if (e.Index == 0)
            {
                //if they're saying it's their off day, reset/disable spinner
                if (e.NewValue == CheckState.Checked)
                {
                    resetSpinners();
                    spinnersEnabledState(false);
                }

                //otherwise, it's not their off day so enable the spinner
                else
                {
                    spinnersEnabledState(true);
                }
            }
        }

        public bool CollectionNoChecks()
        {
            //determine if no boxes are checked
            return view.ProductivityControl.CheckBoxCollection.CheckedItems.Count == 0 &&
                   view.LearningControl.CheckBoxCollection.CheckedItems.Count == 0 &&
                   view.ExerciseControl.CheckBoxCollection.CheckedItems.Count == 0;
        }

        public bool OffDayChecked()
        {
            //determine if it's their off day
            return view.ProductivityControl.CheckBoxCollection.GetItemCheckState(0) == CheckState.Checked ||
                   view.LearningControl.CheckBoxCollection.GetItemCheckState(0) == CheckState.Checked ||
                   view.ExerciseControl.CheckBoxCollection.GetItemCheckState(0) == CheckState.Checked;
        }

        public bool CollectionCanCheck()
        {
            //able to be checked if an item isn't already checked
            return view.ProductivityControl.CheckBoxCollection.CheckedItems.Count < 1 &&
                   view.LearningControl.CheckBoxCollection.CheckedItems.Count < 1 &&
                   view.ExerciseControl.CheckBoxCollection.CheckedItems.Count < 1;
        }

        public void ProductivityTreeSelect(object sender, System.EventArgs e)
        {
            //hide current panel and display productivity panel
            view.displayProductivityPanel();
        }

        public void LearningTreeSelect(object sender, System.EventArgs e)
        {
            //hide current panel and display learning panel
            view.displayLearningPanel();
        }

        public void ExerciseTreeSelect(object sender, System.EventArgs e)
        {
            //hide current panel and display exercise panel
            view.displayExercisePanel();
        }

        // Events used when chopping down a tree
        public void ProductivityTreeRemove(object sender, System.EventArgs e)
        {
            //change last-updated date
            model.ProductivityDate = model.ProductivityDate.AddDays(1);
            
            // Create a new tree
            model.ProductivityTree = new Tree(TreeTypeEnum.ProductivityTree);
            view.productivityTreeControl.Tree = model.ProductivityTree;
            view.productivityTreeControl.Invalidate();
            BeginSelectMode(sender, e);

            //update view
            view.displayHomePanel();
        }

        public void LearningTreeRemove(object sender, System.EventArgs e)
        {
            //change last-updated date
            model.LearningDate = model.LearningDate.AddDays(1);
            
            // Create a new tree
            model.LearningTree = new Tree(TreeTypeEnum.LearningTree);
            view.learningTreeControl.Tree = model.LearningTree;
            view.learningTreeControl.Invalidate();
            BeginSelectMode(sender, e);

            //update view
            view.displayHomePanel();
        }

        public void ExerciseTreeRemove(object sender, System.EventArgs e)
        {
            //change last-updated date
            model.ExerciseDate = model.ExerciseDate.AddDays(1);
            
            // Create a new tree
            model.ExerciseTree = new Tree(TreeTypeEnum.ExerciseTree);
            view.exerciseTreeControl.Tree = model.ExerciseTree;
            view.exerciseTreeControl.Invalidate();
            BeginSelectMode(sender, e);

            //update view
            view.displayHomePanel();
        }

        public void NextDayButton_Click(object sender, EventArgs e)
        {
            //simulate a passing day, update model and view
            model.CurrentDate = model.CurrentDate.AddDays(1);
            view.displayHomePanel();
            view.HomeDate.Font = ChangeFontSize(view.HomeDate.Font, view.HomeDateDefaultFontSize *2);
            view.HomeDateTimer.Start();
        }

        public void BeginAxeMode(object sender, EventArgs e)
        {
            // Update the view to reflect that it is going to chop a tree.
            view.AxeIcon.Image = AxeModeAxe;
            view.HomeNextDay.Visible = false;
            view.Cursor = axeCursor;
            view.AxeIcon.Image = AxeModeEndAxe;
            view.AxeIcon.BackgroundImage = null;

            // Remove the old events that shouldn't be used when in axe mode
            view.productivityTreeControl.Click -= new EventHandler(this.ProductivityTreeSelect);
            view.exerciseTreeControl.Click -= new EventHandler(this.ExerciseTreeSelect);
            view.learningTreeControl.Click -= new EventHandler(this.LearningTreeSelect);
            view.AxeIcon.Click -= new EventHandler(this.BeginAxeMode);
            view.AxeIcon.MouseEnter -= new EventHandler(this.AddSelectModeAxeShadow);
            view.AxeIcon.MouseLeave -= new EventHandler(this.RemoveSelectModeAxeShadow);

            // Add the new events that need to be added for axe mode
            view.productivityTreeControl.Click += new EventHandler(this.ProductivityTreeRemove);
            view.exerciseTreeControl.Click += new EventHandler(this.ExerciseTreeRemove);
            view.learningTreeControl.Click += new EventHandler(this.LearningTreeRemove);
            view.AxeIcon.Click += new EventHandler(this.BeginSelectMode);
            view.AxeIcon.MouseEnter += new EventHandler(this.AddAxeModeEndAxe);
            view.AxeIcon.MouseLeave += new EventHandler(this.RemoveAxeModeEndAxe);
        }

        public void BeginSelectMode(object sender, EventArgs e)
        {
            // Update the view to reflect that is no longer going to chop a tree.
            view.AxeIcon.Image = SelectModeAxe;
            view.HomeNextDay.Visible = true;
            view.Cursor = Cursors.Default;
            view.AxeIcon.BackgroundImage = SelectModeAxeShadow;

            // Remove the old events that were being used in axe mode
            view.productivityTreeControl.Click -= new EventHandler(this.ProductivityTreeRemove);
            view.exerciseTreeControl.Click -= new EventHandler(this.ExerciseTreeRemove);
            view.learningTreeControl.Click -= new EventHandler(this.LearningTreeRemove);
            view.AxeIcon.Click -= new EventHandler(this.BeginSelectMode);
            view.AxeIcon.MouseEnter -= new EventHandler(this.AddAxeModeEndAxe);
            view.AxeIcon.MouseLeave -= new EventHandler(this.RemoveAxeModeEndAxe);

            // Revert the events back to the be handled the as they were before axe mode
            view.productivityTreeControl.Click += new EventHandler(this.ProductivityTreeSelect);
            view.exerciseTreeControl.Click += new EventHandler(this.ExerciseTreeSelect);
            view.learningTreeControl.Click += new EventHandler(this.LearningTreeSelect);
            view.AxeIcon.Click += new EventHandler(this.BeginAxeMode);
            view.AxeIcon.MouseEnter += new EventHandler(this.AddSelectModeAxeShadow);
            view.AxeIcon.MouseLeave += new EventHandler(this.RemoveSelectModeAxeShadow);
        }

        public void AddSelectModeAxeShadow(object sender, EventArgs e)
        {
            view.AxeIcon.BackgroundImage = SelectModeAxeShadow;
        }

        public void AddAxeModeEndAxe(object sender, EventArgs e)
        {
            view.AxeIcon.Image = AxeModeEndAxe;
        }

        public void RemoveSelectModeAxeShadow(object sender, EventArgs e)
        {
            view.AxeIcon.BackgroundImage = null;
        }

        public void RemoveAxeModeEndAxe(object sender, EventArgs e)
        {
            view.AxeIcon.Image = AxeModeAxe;
        }

        void resetSpinners()
        {
            //reset all spinners
            view.ProductivityControl.InputSpinner.Value = 0;
            view.ExerciseControl.InputSpinner.Value = 0;
        }

        void spinnersEnabledState(bool state)
        {
            //enable or disable the spinners
            view.ProductivityControl.InputSpinner.Enabled = state;
            view.ExerciseControl.InputSpinner.Enabled = state;
        }

        void submitEnabledState(bool state)
        {
            //enable or disable the submit buttons
            view.ProductivityControl.SubmitButton.Enabled = state;
            view.LearningControl.SubmitButton.Enabled = state;
            view.ExerciseControl.SubmitButton.Enabled = state;
        }

        public void resetCollectionBoxes()
        {
            //reset and unselect boxes in productivity panel
            for (int i = 0; i < view.ProductivityControl.CheckBoxCollection.Items.Count; i++)
            {
                view.ProductivityControl.CheckBoxCollection.SetSelected(i, false);
                view.ProductivityControl.CheckBoxCollection.SetItemCheckState(i, CheckState.Unchecked);
            }

            //reset and unselect boxes in learning panel
            for (int i = 0; i < view.LearningControl.CheckBoxCollection.Items.Count; i++)
            {
                view.LearningControl.CheckBoxCollection.SetSelected(i, false);
                view.LearningControl.CheckBoxCollection.SetItemCheckState(i, CheckState.Unchecked);
            }

            //reset and unselect boxes in exercise panel
            for (int i = 0; i < view.ExerciseControl.CheckBoxCollection.Items.Count; i++)
            {
                view.ExerciseControl.CheckBoxCollection.SetSelected(i, false);
                view.ExerciseControl.CheckBoxCollection.SetItemCheckState(i, CheckState.Unchecked);
            }
        }
        #endregion

        // It is not possible to change the size of a font of a label without creating a new font object to assign to the label.
        // This function creates a new font object that is the same as the font object passed in, except with a different size.
        static public Font ChangeFontSize(Font font, float fontSize)
        {
            if (font != null)
            {
                float currentSize = font.Size;
                if (currentSize != fontSize)
                {
                    font = new Font(font.Name, fontSize,
                        font.Style, font.Unit,
                        font.GdiCharSet, font.GdiVerticalFont);
                }
            }
            return font;
        }
    }
}
